// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/protobuf-expectation/descriptor-proto-printer.h"

#include "glog/logging.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/text_format.h"
#include "gtest/gtest.h"

using std::string;

using google::protobuf::FileDescriptorProto;
using google::protobuf::TextFormat;

namespace google {
namespace protobuf_expectation {

class DescriptorProtoPrinterTest : public ::testing::Test {
 protected:
  FileDescriptorProto file_descriptor_;
  string output_;
};

TEST_F(DescriptorProtoPrinterTest, EmptyFileDescriptorProto) {
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
EXPECT_EQ("// This proto file is generated by test.\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, Dependencies) {
  CHECK(TextFormat::ParseFromString(
      "dependency: \"foo.proto\" "
      "dependency: \"foo/bar.proto\" "
      "dependency: \"foo/bar/baz.proto\"",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "import \"foo.proto\";\n"
            "import \"foo/bar.proto\";\n"
            "import \"foo/bar/baz.proto\";\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, Options) {
  CHECK(TextFormat::ParseFromString(
      "options: {"
      "  java_package: \"com.google.protobuf\" "  // string
      "  optimize_for: SPEED "  // enum
      "  java_multiple_files: true "  // bool
      "}",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "option java_package = \"com.google.protobuf\";\n"
            "option optimize_for = SPEED;\n"
            "option java_multiple_files = true;\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, Package) {
  CHECK(TextFormat::ParseFromString(
      "package: \"foo\"",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "package foo;\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, Message) {
  CHECK(TextFormat::ParseFromString(
      "message_type: {"
      "  name: \"Foo\" "
      "  field: { name: \"bar\" number: 1 label: LABEL_OPTIONAL "
      "           type: TYPE_INT32 }"
      "}",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "message Foo {\n"
            "  optional int32 bar = 1;\n"
            "}\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, NestedMessage) {
  CHECK(TextFormat::ParseFromString(
      "message_type: {"
      "  name: \"Foo\" "
      "  field: { name: \"bar\" number: 1 label: LABEL_OPTIONAL "
      "           type: TYPE_MESSAGE type_name: \"Bar\" }"
      "  nested_type: {"
      "    name: \"Bar\" "
      "    field: { name: \"baz\" number: 2 label: LABEL_OPTIONAL "
      "             type: TYPE_STRING }"
      "  }"
      "}",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "message Foo {\n"
            "  optional Bar bar = 1;\n\n"
            "  message Bar {\n"
            "    optional string baz = 2;\n"
            "  }\n"
            "}\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, Labels) {
  CHECK(TextFormat::ParseFromString(
      "message_type: {"
      "  name: \"Foo\" "
      "  field: { name: \"bar\" number: 1 label: LABEL_OPTIONAL "
      "           type: TYPE_INT32 }"
      "  field: { name: \"baz\" number: 2 label: LABEL_REQUIRED "
      "           type: TYPE_INT32 }"
      "  field: { name: \"qux\" number: 3 label: LABEL_REPEATED "
      "           type: TYPE_INT32 }"
      "}",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "message Foo {\n"
            "  optional int32 bar = 1;\n"
            "  required int32 baz = 2;\n"
            "  repeated int32 qux = 3;\n"
            "}\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, Types) {
  CHECK(TextFormat::ParseFromString(
      "message_type: {"
      "  name: \"Foo\" "
      "  field: { name: \"a\" number: 1 label: LABEL_OPTIONAL "
      "           type: TYPE_DOUBLE }"
      "  field: { name: \"b\" number: 2 label: LABEL_OPTIONAL "
      "           type: TYPE_FLOAT }"
      "  field: { name: \"c\" number: 3 label: LABEL_OPTIONAL "
      "           type: TYPE_INT64 }"
      "  field: { name: \"d\" number: 4 label: LABEL_OPTIONAL "
      "           type: TYPE_UINT64 }"
      "  field: { name: \"e\" number: 5 label: LABEL_OPTIONAL "
      "           type: TYPE_INT32 }"
      "  field: { name: \"f\" number: 6 label: LABEL_OPTIONAL "
      "           type: TYPE_FIXED64 }"
      "  field: { name: \"g\" number: 7 label: LABEL_OPTIONAL "
      "           type: TYPE_FIXED32 }"
      "  field: { name: \"h\" number: 8 label: LABEL_OPTIONAL "
      "           type: TYPE_BOOL }"
      "  field: { name: \"i\" number: 9 label: LABEL_OPTIONAL "
      "           type: TYPE_STRING }"
      // TYPE_GROUP is not implemented.
      "  field: { name: \"k\" number: 11 label: LABEL_OPTIONAL "
      "           type: TYPE_MESSAGE type_name: \"Bar\" }"
      "  field: { name: \"l\" number: 12 label: LABEL_OPTIONAL "
      "           type: TYPE_BYTES }"
      "  field: { name: \"m\" number: 13 label: LABEL_OPTIONAL "
      "           type: TYPE_UINT32 }"
      "  field: { name: \"n\" number: 14 label: LABEL_OPTIONAL "
      "           type: TYPE_ENUM type_name: \"Baz\" }"
      "  field: { name: \"o\" number: 15 label: LABEL_OPTIONAL "
      "           type: TYPE_SFIXED32 }"
      "  field: { name: \"p\" number: 16 label: LABEL_OPTIONAL "
      "           type: TYPE_SFIXED64 }"
      "  field: { name: \"q\" number: 17 label: LABEL_OPTIONAL "
      "           type: TYPE_SINT32 }"
      "  field: { name: \"r\" number: 18 label: LABEL_OPTIONAL "
      "           type: TYPE_SINT64 }"
      "}",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "message Foo {\n"
            "  optional double a = 1;\n"
            "  optional float b = 2;\n"
            "  optional int64 c = 3;\n"
            "  optional uint64 d = 4;\n"
            "  optional int32 e = 5;\n"
            "  optional fixed64 f = 6;\n"
            "  optional fixed32 g = 7;\n"
            "  optional bool h = 8;\n"
            "  optional string i = 9;\n"
            // group is not implemented.
            "  optional Bar k = 11;\n"  // message
            "  optional bytes l = 12;\n"
            "  optional uint32 m = 13;\n"
            "  optional Baz n = 14;\n"  // enum
            "  optional sfixed32 o = 15;\n"
            "  optional sfixed64 p = 16;\n"
            "  optional sint32 q = 17;\n"
            "  optional sint64 r = 18;\n"
            "}\n",
            output_);
}

TEST_F(DescriptorProtoPrinterTest, Combination) {
  CHECK(TextFormat::ParseFromString(
      "dependency: \"foo.proto\" "
      "options: {"
      "  java_package: \"com.google.protobuf\" "
      "}"
      "package: \"foo\""
      "message_type: {"
      "  name: \"Foo\" "
      "  field: {"
      "    name: \"bar\" "
      "    number: 1 "
      "    label: LABEL_OPTIONAL "
      "    type: TYPE_MESSAGE "
      "    type_name: \"Bar\" "
      "  }"
      "  nested_type: {"
      "    name: \"Bar\" "
      "    field: {"
      "      name: \"baz\" "
      "      number: 2 "
      "      label: LABEL_OPTIONAL "
      "      type: TYPE_STRING "
      "    }"
      "  }"
      "}",
      &file_descriptor_));
  EXPECT_TRUE(PrintFileDescriptorProtoToString(file_descriptor_, "test",
                                               &output_));
  EXPECT_EQ("// This proto file is generated by test.\n\n"
            "import \"foo.proto\";\n\n"
            "option java_package = \"com.google.protobuf\";\n\n"
            "package foo;\n\n"
            "message Foo {\n"
            "  optional Bar bar = 1;\n\n"
            "  message Bar {\n"
            "    optional string baz = 2;\n"
            "  }\n"
            "}\n",
            output_);
}

}  // namespace protobuf_expectation
}  // namespace google
